package demo1;

//理解多态：
//1. 向上转型。 多态性：父类引用指向子类对象
//2. 方法重写。
//3. 通过父类引用指向子类对象，调用子类重写的方法。

class Animal {
    String name;
    int age;
    public void eat() {
        System.out.println("Animal eat");
    }
    public void sleep() {
        System.out.println("Animal sleep");
    }
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }
    public void bark() {
        System.out.println("Dog 旺旺！");
    }
    @Override
    public void eat() {
        System.out.println("Dog 正在吃狗粮");
    }
}
class Bird extends Animal {
    public Bird(String name, int age) {
        super(name, age);
    }
    public void fly() {
        System.out.println("Bird 正在飞");
    }
}
public class Polymorphism_basics {
    public static void main(String[] args) {
        //有三种方式可以发生向上转型
        //1. 直接赋值
        Animal animal1 = new Dog("旺财", 3);
        //animal1.eat(); 只能调用父类
        // animal1.bark(); 报错，因为animal1是Animal类型，没有bark方法
        //间接赋值也可以
        Dog dog1 = new Dog("旺财", 3);
        Animal animal2 = dog1;

        //2. 方法传参的方式
        func1(dog1);
        func1(animal2);

        //3. 返回值
        Animal animal3 = func2();

        //向上转型其实就是创建一个子类对象，将其当成父类对象来使用
        //可以理解为Animal是一个父类，但可以引用一个子类对象，因为父类是大范围的，子类是小范围，可以发生小范围向大范围的转换
        //向上转型的优点：使代码实现更加简单灵活
        //向上转型的缺点：不能调用子类特有的方法
        System.out.println("======================");

        //如果此时再在Dog里写一个eat方法
        //animal1.eat(); 调用的就是Dog的eat方法
        //这是因为Animal和Dog类里的eat方法构成了重写 这里就是通过父类引用调用子类的重写方法
        //方法重写的三个条件：
        //1. 方法名相同
        //2. 参数列表相同(个数，类型，顺序)
        //3. 返回值类型必须相同
        //它们构成了重写，而调用时调用了子类的方法，这就叫做运行时绑定 或 动态绑定，这是多态的基础！！！

        //静态绑定/编译时绑定：方法的重载。在编译时，通过区分参数列表，在编译时，就能知道调用的是哪个方法
        //而动态绑定是在运行时，才发现了方法重写，所以会直接调用子类

        //重写的注意事项：
        //1. 被private，static，final修饰的方法不能被重写
        //2. 访问修饰限定符private < default < protected < public, 子类方法的访问权限>=父类方法
        //3. 方法的返回值可以不同，但必须是父子类关系，也叫做协变类型
        //4. 构造方法不能重写

        //静态绑定：也称为前期绑定(早绑定)，即在编译时，根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
        //动态绑定：也称为后期绑定(晚绑定)，即在编译时，不能确定方法的行为，需要等到程序运行时，才能够确定具体调用那个类的方法。

        //父类原有的功能不能满足现在的需求，可以进行重写


        //向下转型：子类对象强制转换成父类对象
        Animal animal4 = new Bird("知更鸟", 18);
        //animal4.fly(); 这里会报错，因为只能调用父类的方法
        if(animal4 instanceof Bird) {
            Bird bird1 = (Bird)animal4;
            bird1.fly(); //此时就可以调用fly了，这就是向下转型。向下转型不安全，所以要用关键字
            //Dog dog2 = (Dog)animal4; 这样会报错，因为animal4是Bird类型向上转型来的，不能强制转换成Dog类型
            //使用instanceof关键字判断一下animal4是否是Bird向上转型来的
        }

        System.out.println("======================");
    }
    public static void func1(Animal animal) {
        //因为Animal类里的方法都是非静态的，所以这里调用不了
    }
    public static Animal func2() {
        //明确了返回值类型是Animal，就必须返回Animal类或Animal的子类
        return new Dog("旺财", 3);
        //更建议这种写法，new一个谁都可以。
    }
}